package Server;

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.hibernate.Query;
import General.ClientDetails;
import Messages.AddToWatcherMsg;
import Messages.BadDetailsMsg;
import Messages.CreateFolderMsg;
import Messages.FileChangeMsg;
import Messages.IsNotExsitMsg;
import Messages.Msg;
import Messages.MsgType;
import Messages.SendFileMsg;
import Messages.ShareMsg;
import Messages.StartConnectionMsg;
import Messages.StartMsg;
import Messages.StopMsg;
import Messages.WhichActionMsg;
import ServerDB.Folder;
import ServerDB.MapFileServer;
import ServerDB.User;

/**
 *	run the client connection, all the clients updates are sending to 
 *	here, and from here it is update in the db, and on to other users 
 * @author Liel && Guy
 *
 */
public class RunClientProtocol implements Runnable {

	Socket connection = null;
	ObjectOutputStream out;
	ObjectInputStream in;
	Msg response;
	Msg request;
	ServerProtocol server;
	ArrayList<Msg> messages;

	int port = 8989;
	org.hibernate.classic.Session sessionHibernate;
	/**
	 * the constructor 
	 * @param connection - the connection received from server
	 * @param sessionHibernate - the session to update DB from server
	 */
	public RunClientProtocol(Socket connection, org.hibernate.classic.Session sessionHibernate){
		this.connection = connection;
		this.sessionHibernate = sessionHibernate;
		server = ServerProtocol.getInstance();
		messages = new ArrayList<Msg>();
	}

	/**
	 * the main method of this thread
	 */
	public void run()
	{
		ClientDetails clientDetails=null;
		String fileName=null;
		String folderName=null;
		String fileCheckSum=null;
		int folderID = 0;

		try{
			FileChangeMsg fileChangeMsg=null;
			File file;
			out = new ObjectOutputStream(connection.getOutputStream());
			out.flush();
			in = new ObjectInputStream(connection.getInputStream());
			//send start message to client
			request=new StartMsg();
			sendMessage(request);
			do
			{
				try
				{
					//get first message from client
					response = (Msg)in.readObject();
					switch(response.getType())
					{
					//start connection is the first message that the client sends
					case START_CONNECTION:
						StartConnectionMsg startMsg = (StartConnectionMsg) response;
						clientDetails = new ClientDetails(startMsg.getUserName(), startMsg.getPassword(),
								startMsg.getMainFolder(), startMsg.getEmail(), null);
						//check if user is in the DB
						boolean isValid = server.IsUserValid(clientDetails);
						if (isValid)
							request=new WhichActionMsg();
						else
							request = new BadDetailsMsg();
						
						sendMessage(request);
						break;
					//if this is create new file
					case CREATE:
						fileChangeMsg = (FileChangeMsg) response;
						fileName = fileChangeMsg.getFileName();
						folderName = fileChangeMsg.getFolder();
						//if file already in the server with same details
						if (server.fileExist(clientDetails, fileName, folderName)){
							request = getRequest();
						}
						else{
							fileCheckSum = fileChangeMsg.getFileHash();
							file = server.getFile(fileCheckSum);
							if (file==null){
								//the file is not in the server
								request = new IsNotExsitMsg();	
							}
							else{
								//the file is in the server
								server.addFileToTable(clientDetails, fileChangeMsg, file, false);
								request = getRequest();								
							}
						}
						sendMessage(request);
						break;
					//if it is file modify change
					case MODIFY:
						fileChangeMsg = (FileChangeMsg) response;
						//if file already in the server with same details
						if (checkIfEqual(clientDetails, fileChangeMsg)){
							request = getRequest();
						}
						else{//this is really modify event							
							server.DeleteFile(clientDetails,fileChangeMsg);
							fileCheckSum = fileChangeMsg.getFileHash();
							file = server.getFile(fileCheckSum);
							if (file==null){
								//the file is not in the server
								request = new IsNotExsitMsg();	
							}
							else{
								//the file is in the server
								//update file in DB
								server.addFileToTable(clientDetails, fileChangeMsg, file, false);								request = getRequest();								
							}	
						}
						sendMessage(request);
						break;
					//if this is delete file
					case DELETE:
						fileChangeMsg = (FileChangeMsg) response;
						server.DeleteFile(clientDetails, fileChangeMsg);
						request = getRequest();
						sendMessage(request);
						break;
					//the client send the file
					case SEND_FILE:
						SendFileMsg sendFilemsg = (SendFileMsg) response;
						//create the file
						File newFile = server.createFile(sendFilemsg.getFile(), fileName);
						//add the file to check sum map
						server.addFile(fileCheckSum, newFile);
						//add the file to table
						server.addFileToTable(clientDetails, fileChangeMsg, newFile, false);
						request=getRequest();
						sendMessage(request);
						break;
					//create new folder
					case CREATE_FOLDER:
						CreateFolderMsg createFolderMsg=(CreateFolderMsg)response;
						//check if folder exist in server
						boolean flagFolderExist=server.isFolderExist(clientDetails, createFolderMsg.getFolderName());
						//if not - create new folder
						if(!flagFolderExist)
						{
							folderID=server.addFolderToDB(createFolderMsg.getFolderName(), clientDetails);
							//send the client that he needs to watch the folder
							request=new AddToWatcherMsg(createFolderMsg.getFolderName(), folderID);
						}
						else
						{
							request=getRequest();
						}

						sendMessage(request);
						break;
					//if this is share message
					case SHARE:
						ShareMsg shareMsg=(ShareMsg)response;
						//share folder with all other users
						server.shareFolder(shareMsg.getUsers(), shareMsg.getFolderName(), clientDetails);
						request=getRequest();
						sendMessage(request);
						break;
					//the client send it after he finished adding new folder to watcher
					case FINISH_WATCH:
						request = getRequest();
						sendMessage(request);
						break;
					case STOP:
						break;
					}
				}
				catch(ClassNotFoundException classnot)
				{
					System.err.println("Data received in unknown format");
				}
			}while(response.getType()!=MsgType.STOP);
		}
		catch(IOException ioException)
		{
			ioException.printStackTrace();
		}
		finally
		{	
			//4: Closing connection
			try
			{
				Thread.sleep(1000);
				if (in!=null)
					in.close();
				if (out!=null)
					out.close();
				if (connection!=null)
					connection.close();
			}
			catch(IOException ioException)
			{
				ioException.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	//check if the same record already in DB
	private boolean checkIfEqual(ClientDetails cd, FileChangeMsg msg){
		String fileName = msg.getFileName();
		String folderName = msg.getFolder();
		String userName = cd.getUserName();
		String strQuery="FROM User WHERE USER_NAME =:userNameQry";
		Query query=sessionHibernate.createQuery(strQuery);
		query.setParameter("userNameQry", userName);
		System.out.println("After SQL 1 line ->"+userName);
		@SuppressWarnings("unchecked")
		List<User> listUsers=query.list();
		//		itUser.hasNext()
		if (listUsers.size()==0){
			System.out.println("000000000000000000");
			System.out.println(userName);
			System.out.println("000000000000000000");
			return false;
		}
		User user = listUsers.get(0);
		Set<Folder> listOfFolders = user.getFolders();
		System.out.println(user.getUserName() + "   " + listOfFolders.size());
		Folder currentFolder=null;
		for (Folder folder: listOfFolders){
			if (folder.getFolderName().equals(folderName))
				currentFolder = folder;
		}
		if (currentFolder==null){
			System.out.println("wrong folder name!!!!!!!");
			return false;
		}
		int folderId = currentFolder.getFolderID();
		strQuery="FROM MapFileServer WHERE FOLDER_ID =:folderIdQry AND FILE_NAME =:fileNameQry";
		query=sessionHibernate.createQuery(strQuery);
		System.out.println("data = " + folderId + " " + fileName);
		query.setParameter("folderIdQry", folderId);
		query.setParameter("fileNameQry", fileName);
		System.out.println("After SQL 2 line");
		@SuppressWarnings("unchecked")
		List<MapFileServer> files =query.list();
		System.out.println(files.size());
		if (files.size()==0)
			return false;
		MapFileServer file = files.get(0);
		String checkSum = file.getCheckSum();
		if (checkSum.equals(msg.getFileHash()))
			return true;

		return false;
	}
	private Msg getRequest(){
		return new WhichActionMsg();
	}
	
	private void sendMessage(Msg msg)
	{
		if (msg==null){
			msg = new StopMsg();
		}
		try{
			out.writeObject(msg);
			out.flush();
			System.out.println("server>" + msg);
		}
		
		catch(IOException ioException){
			ioException.printStackTrace();
		}
	}
}

